/*
 * VigenereBreaker.cpp
 *
 *  Created on: Nov 28, 2011
 *      Author: belu
 */

#include "VigenereBreaker.h"

VigenereBreaker::VigenereBreaker() {
}

VigenereBreaker::~VigenereBreaker() {
}

/*
 * Devuelve un vector con las distancias de las cadenas de longitud cantCaracteres
 * (indicado parametro) repetidas.  
 */
vector<unsigned long> VigenereBreaker::compararCaracteres(string mge, int cantCaracteres){
	vector<unsigned long> distancias;
	char* mjeChar = new char[mge.size()+1];
	strcpy(mjeChar, mge.c_str());
	char* tmp = new char[cantCaracteres];
	char* tmp2= new char[cantCaracteres];
	unsigned long dist = 0;
	for(int i=0; i<(mge.size()-cantCaracteres); i++){
		for(int j=0; j<cantCaracteres; j++){
			tmp[j] = mjeChar[j+i];			
		}
		for(int j=cantCaracteres; j<mge.size(); j++){
			for(int z=0; z<cantCaracteres; z++){
				if(i+j+z<=mge.size()){
					tmp2[z] = mjeChar[i+j+z];
				}
			}
			if(strcmp(tmp,tmp2)==0){
				dist = j; 
				distancias.push_back(dist);
				dist = 0;
			}			
		}
	}
	return distancias;
}

/*
 * Busca los mcd entre dos numeros. 
 */
unsigned long long VigenereBreaker::mcd(unsigned long long a, unsigned long long b) {
	a = a < 0 ? a * -1 : a;
	b = b < 0 ? b * -1 : b;
	a = a % b;
	if (a > 0)
		return mcd(b, a);
	else
		return b;
}

bool VigenereBreaker::isPrime(unsigned long num) {
	unsigned long res = 0;
	unsigned long divs = 2;
	unsigned long mitad;

	if (num > 2) {
		mitad = floor((double) num / 2);
		do {
			res = (num % divs != 0);
			divs++;

		} while ((res != 0) && divs <= mitad);
	} else {
		res = 1;
	}
	return (res == 1?true:false);
}

string VigenereBreaker::obtenerCriptograma(string mge, unsigned long intervalo, unsigned long caracterInicial){
	char* mjeChar = new char[mge.size()+1];
	strcpy(mjeChar, mge.c_str());
	char* mjeReturn = new char[mge.size()+1];
	
	int posAnterior = caracterInicial;
	int recortar = 0;
	
	for(int i=0; i<mge.size(); i++){
		mjeReturn[i] = mjeChar[posAnterior];
		posAnterior += intervalo;
		if(posAnterior>=mge.size()){
			recortar = i;
			i=mge.size();
		}
	}
	string criptograma (mjeReturn);
	criptograma.resize(recortar+1);
	return criptograma;
}

vector<int> VigenereBreaker::calcularFrecuencias(string criptograma, string alfabeto){
	vector<int> frecuencias;
	char* mjeChar = new char[criptograma.size()+1];
	strcpy(mjeChar, criptograma.c_str());
	
	char* alfabetoChar = new char[alfabeto.size()+1];
	strcpy(alfabetoChar, alfabeto.c_str());
	
	for(int i=0; i<alfabeto.size();i++){
		frecuencias.push_back(0);
	}
	
	for(int i=0; i<criptograma.size(); i++){
		for(int j=0; j<alfabeto.size(); j++){
			if(mjeChar[i] == alfabetoChar[j]){
				frecuencias[j] += 1;
				j=alfabeto.size();
			}
		}
	}
	return frecuencias;
}

string VigenereBreaker::returnClave(string mge, string alfabeto, unsigned long longClave){
	char* alfabetoChar = new char[alfabeto.size()+1];
	strcpy(alfabetoChar, alfabeto.c_str());
	char* datosChar = new char[longClave];
	int posMayor = 0;
	vector<int> frecuencias;
	vector<int> frecuenciasTmp;
	string criptograma;
		
	for(int i = 0; i<alfabeto.size(); i++){
		frecuencias.push_back(0);
	}
	
	for (int i=0; i<longClave; i++){	
		criptograma = obtenerCriptograma(mge,longClave,i);
		frecuenciasTmp = calcularFrecuencias(criptograma,alfabeto);
		
		for(int j=0; j<frecuencias.size(); j++){
			frecuencias[j] += frecuenciasTmp[j];
		}
	}	
		
	for(int i=0; i<longClave; i++){
		int mayor = 0;
		for(int j=0; j<alfabeto.size(); j++){
			if(mayor<frecuencias.at(j)){
				mayor=frecuencias.at(j);
				posMayor = j;
			}
		}
		datosChar[i] = alfabetoChar[posMayor];
		frecuencias[posMayor] = 0;
	}
	string clave(datosChar);
	clave.resize(longClave);
	return clave;
}

string VigenereBreaker::decryptKasiski(string mge){
	string mjeKasiski;
	vector<unsigned long> distancias;
	string alfabeto;
	
	distancias = compararCaracteres(mge, 4);
	
	unsigned long menor = 0;
	unsigned long valor = 0;
	
	if (distancias.size()>1){
		menor = mcd(distancias.at(0),distancias.at(1));		
		
		for(int i=0; i<distancias.size()-1; i++){
			valor=mcd(distancias.at(i),distancias.at(i+1));
			if(menor>valor){
				menor = valor;
			}
		}
	}
	else {
		if(distancias.size() == 1){
			menor = distancias.at(0); 
		}
	}
	alfabeto = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
	//alfabeto = vigenere->getSymbols();	
	
	string clave = returnClave(mge, alfabeto, menor);
	cout<<"Clave con "<<menor<<" caracteres: "<<clave<<endl;
	
	int primo = menor+1;
	while(!isPrime(primo)){
		primo += 1;
	}
	
	clave = returnClave(mge, alfabeto, primo);
	cout<<"Clave con "<<primo<<" caracteres(primer primo mayor): "<<clave<<endl;
	
	if(menor!=0){
		primo = menor-1;
		while(!isPrime(primo)){
			primo -= 1;
		}
		clave = returnClave(mge, alfabeto, primo);
		cout<<"Clave con "<<primo<<" caracteres(primer primo menor): "<<clave<<endl;		
	}
	
	return mjeKasiski;
}

/*
 * Pasos para obtener la clave mediante Kasiski:
 * 1. Buscar cadenas repetidas en el texto. Y calcular las distancias. 
 * 2. Hallar mcd entre esas distancias.
 * 3. Obtener el menor mcd (n). La clave tendra n caracteres
 * 4. Dividir el criptograma original en n criptogramas. Por ejemplo, si n=4
 * 	  cA = tomamos el 1, 5, 9.. caracteres
 * 	  cB = el 2,6,10.. caracter
 *    etc..
 * 5. Buscar las frecuencias con la que aparecen las letras de cada criptograma
 *    en el alfabeto definido en Vigenere.
 * 6. Armar la clave con las primeras n letras m�s frecuentes. 
 * 
 */
bool VigenereBreaker::breakByKasiskiAttack(string filePath, string filePathKasiski) {
	//vigenere = new Vigenere();
	ifstream encryptedFile;
	encryptedFile.open(filePath.c_str());
	
	ofstream plainFile;
	plainFile.open(filePathKasiski.c_str(),fstream::out|fstream::trunc);

	if (plainFile.is_open() == false || encryptedFile.is_open() == false) {
		return false;
	}
	
	string aLine;
	string bLine;
	while (getline(encryptedFile, aLine)) {		
		bLine = decryptKasiski(aLine);
		plainFile << bLine << endl;
	}

	plainFile.flush();
	plainFile.close();

	encryptedFile.close();

	return true;

}
